from typing import Any, Dict
from src.models.knowledge_graph import KnowledgeGraphEdge, KnowledgeGraphNode

class KnowledgeGraphService:
    """
    Service for managing a knowledge graph.
    """

    def __init__(self):
        self.graph = None  # Placeholder for actual graph instance

    def create_or_update_relationship(self, relationship: Dict[str, Any]) -> None:
        """
        Creates or updates a relationship in the knowledge graph.
        """
        pass  # Implementation goes here
"""AI知识库管理服务
提供完整的AI知识库管理功能，包括知识采集、存储、检索、版本控制、向量嵌入和语义搜索等
"""

import asyncio
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from src.services.knowledge_base_service import KnowledgeBaseService, KnowledgeBaseItem, KnowledgeBaseQuery, SearchSuggestion

__all__ = ['KnowledgeBaseService', 'KnowledgeBaseItem', 'KnowledgeBaseQuery', 'SearchSuggestion']

import os
import logging
import traceback
from typing import List, Dict, Any, Optional, Tuple
from datetime import datetime
import uuid
from pydantic import BaseModel, Field
from sqlalchemy.orm import Session
from sqlalchemy import create_engine, and_, or_, Integer
from sqlalchemy.orm import sessionmaker
from src.models.knowledge_graph import KnowledgeGraphNode, KnowledgeGraphEdge
from src.config.settings import settings
from sqlalchemy import URL
from src.exceptions import DatabaseError, ServiceError
from src.services.service_wrappers import get_knowledge_graph_service
import json
import asyncio
from concurrent.futures import ThreadPoolExecutor
import numpy as np
from functools import lru_cache
import hashlib

# 为Pylance提供类型提示
from src.config.settings import Settings
settings: Settings

# 尝试导入嵌入模型
try:
    from sentence_transformers import SentenceTransformer
    SENTENCE_TRANSFORMERS_AVAILABLE = True
except ImportError:
    SENTENCE_TRANSFORMERS_AVAILABLE = False
    SentenceTransformer = None  
    logging.warning("Sentence Transformers库未安装，将禁用语义搜索功能")

# 尝试导入FAISS
try:
    import faiss
    FAISS_AVAILABLE = True
except ImportError:
    FAISS_AVAILABLE = False
    faiss = None  
    logging.warning("FAISS库未安装，将禁用语义搜索功能")

logger = logging.getLogger(__name__)


# 为Pylance提供类型提示
if False:
    # 仅用于类型提示，不会实际执行
    class KnowledgeBaseService:
        pass

# 为Pylance提供方法类型提示
if False:  # pragma: no cover
    def _extract_entities_and_relationships(self, item: KnowledgeBaseItem) -> Tuple[List[Dict], List[Dict]]:
        """
        从知识库条目中提取实体和关系
        
        Args:
            item: 知识库条目
            
        Returns:
            (实体列表, 关系列表)
        """
        entities = []
        relationships = []
        
        # 创建一个表示该知识条目的实体
        entities.append({
            "id": f"kb_item_{item.id}",
            "name": item.title,
            "type": "KnowledgeBaseItem",
            "properties": {
                "content": item.content,
                "category": item.category,
                "tags": item.tags,
                "summary": item.summary,
                "language": item.language,
                "author": item.author,
                "status": item.status
            }
        })
        
        # 从内容中提取其他实体（这里使用简单关键词提取，实际应用中可以使用NLP技术）
        words = item.content.split()
        for i, word in enumerate(words):
            if len(word) > 3:  # 简单过滤短词
                entities.append({
                    "id": f"word_{hash(word)}",
                    "name": word,
                    "type": "Keyword",
                    "properties": {}
                })
                
                # 创建与主条目的关系
                relationship = {
                    "source": f"kb_item_{item.id}",
                    "target": f"word_{hash(word)}",
                    "type": "CONTAINS_KEYWORD",
                    "properties": {
                        "position": i
                    }
                }
                # 调用知识图谱服务创建或更新关系
                self.kg_service.create_or_update_relationship(relationship)
        
        return entities, relationships

    def get_related_knowledge(self, item_id: str, limit: int = 5) -> List[KnowledgeBaseItem]:
        """
        获取相关知识条目
        
        Args:
            item_id: 知识条目ID
            limit: 返回结果数量限制
            
        Returns:
            相关知识条目列表
        """
        try:
            # 首先获取当前条目
            item = self.get_knowledge_item(item_id)
            if not item:
                return []
            
            # 构建查询
            query = KnowledgeBaseQuery(
                query_text=item.title,
                category=item.category,
                tags=item.tags,
                limit=limit + 1,  # +1 because the item itself might be included
                status="published"
            )
            
            # 执行查询
            related_items = self.search_knowledge_items(query)
            
            # 过滤掉当前条目本身
            filtered_items = [i for i in related_items if i.id != item_id]
            
            # 限制数量
            return filtered_items[:limit]
            
        except Exception as e:
            logger.error(f"获取相关知识失败: {e}")
            return []

    def get_knowledge_graph_entities(self, item_id: str) -> List[Dict[str, Any]]:
        """
        获取与知识条目相关的知识图谱实体
        
        Args:
            item_id: 知识条目ID
            
        Returns:
            知识图谱实体列表
        """
        try:
            if not self.kg_service or not self.kg_service.graph:
                return []
            
            # 查询与该知识条目相关的实体
            cypher_query = """
            MATCH (n:KnowledgeBaseItem {id: $item_id})-[r]-(related)
            RETURN related, r
            LIMIT 10
            """
            
            results = self.kg_service.graph.run(cypher_query, item_id=f"kb_item_{item_id}").data()
            
            entities = []
            for record in results:
                related_node = record['related']
                relationship = record['r']
                
                entities.append({
                    "node": {
                        "id": related_node.identity,
                        "labels": list(related_node.labels),
                        "properties": dict(related_node)
                    },
                    "relationship": {
                        "type": type(relationship).__name__,
                        "properties": dict(relationship)
                    }
                })
            
            return entities
            
        except Exception as e:
            logger.error(f"获取知识图谱实体失败: {e}")
            return []
    
class KnowledgeBaseItem(BaseModel):
    """AI知识库条目模型"""
    id: str = Field(default_factory=lambda: str(uuid.uuid4()))  # 确保始终有ID
    title: str
    content: str
    category: str = "default"
    tags: List[str] = []
    author: Optional[str] = None
    status: str = "draft"  # draft, published, archived
    version: int = 1
    metadata: Dict[str, Any] = {}
    embedding: Optional[List[float]] = None  # 嵌入向量
    language: str = "zh"  # 语言标识
    summary: Optional[str] = None  # 内容摘要
    related_items: List[str] = []  # 相关条目ID列表
    view_count: int = 0  # 查看次数
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None
    published_at: Optional[datetime] = None


class KnowledgeBaseQuery(BaseModel):
    """AI知识库查询模型"""
    query_text: str
    category: Optional[str] = None
    tags: List[str] = []
    author: Optional[str] = None
    status: Optional[str] = None
    language: Optional[str] = None  # 语言过滤
    sort_by: str = "created_at"  # created_at, updated_at, relevance, view_count
    sort_order: str = "desc"  # asc, desc
    limit: int = 10
    offset: int = 0
    semantic_search: bool = False  # 是否启用语义搜索
    hybrid_search: bool = False  # 是否启用混合搜索（关键词+语义）
    fuzzy_search: bool = False  # 是否启用模糊搜索
    spell_check: bool = False  # 是否启用拼写检查
    synonym_expansion: bool = False  # 是否启用同义词扩展
    similarity_threshold: float = 0.5  # 相似度阈值
    include_summary: bool = False  # 是否包含摘要
    include_related: bool = False  # 是否包含相关条目


class SearchSuggestion(BaseModel):
    """搜索建议模型"""
    text: str
    frequency: int = 0


class KnowledgeBaseService:
    """AI知识库管理服务类"""
    
    # 为Pylance提供方法类型提示
    if False:  # pragma: no cover
        def _sync_to_knowledge_graph_async(self, item: KnowledgeBaseItem) -> None: ...
        def _update_search_suggestions(self, query_text: str) -> None: ...
        def get_search_suggestions(self, prefix: str, limit: int = 10) -> List[SearchSuggestion]: ...
        def search_knowledge_items(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]: ...
        def _search_knowledge_items_from_db(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]: ...
        def _semantic_search(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]: ...
        def _hybrid_search(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]: ...
        def _update_vector_index(self, item: KnowledgeBaseItem) -> None: ...
        def _remove_from_vector_index(self, item_id: str) -> None: ...
        def _build_vector_index(self) -> None: ...
        def _generate_cache_key(self, query: KnowledgeBaseQuery) -> str: ...
        def _correct_spelling(self, query_text: str) -> str: ...
        def _expand_with_synonyms(self, query_text: str) -> List[str]: ...
    
    def __init__(self):
        """初始化AI知识库服务"""
        self.engine = create_engine(settings.DATABASE_URL)  
        self.SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=self.engine)
        self.kg_service = get_knowledge_graph_service()
        # 初始化缓存服务
        from src.services.cache_service import cache_service
        self.cache_service = cache_service
        # 初始化线程池用于异步处理
        self.executor = ThreadPoolExecutor(max_workers=4)
        # 初始化嵌入模型
        self.embedding_model = None
        self._initialize_embedding_model()
        # 初始化FAISS向量索引
        self.vector_index = None
        self.id_to_index = {}  # 知识条目ID到向量索引的映射
        self.index_to_id = {}  # 向量索引到知识条目ID的映射
        self._initialize_vector_index()
        # 搜索建议词典
        self.search_suggestions: Dict[str, int] = {}
        
    def _initialize_embedding_model(self):
        """初始化嵌入模型"""
        try:
            if SENTENCE_TRANSFORMERS_AVAILABLE and SentenceTransformer is not None:
                # 这里使用与知识图谱服务相同的模型
                self.embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
                logger.info("嵌入模型初始化成功")
            else:
                logger.warning("Sentence Transformers库未安装，将禁用语义搜索功能")
        except Exception as e:
            logger.warning(f"嵌入模型初始化失败: {e}，将禁用语义搜索功能")
            self.embedding_model = None
    
    def _initialize_vector_index(self):
        """初始化向量索引"""
        try:
            if FAISS_AVAILABLE and self.embedding_model and faiss is not None:
                # 初始化FAISS向量索引 (384维是all-MiniLM-L6-v2模型的输出维度)
                # 使用IndexIDMap支持增量更新操作
                base_index = faiss.IndexFlatL2(384)
                self.vector_index = faiss.IndexIDMap(base_index)
                logger.info("FAISS向量索引初始化成功")
                # 构建现有数据的向量索引
                self._build_vector_index()
            else:
                logger.warning("FAISS库未安装或嵌入模型未初始化，将禁用语义搜索功能")
        except Exception as e:
            logger.warning(f"FAISS向量索引初始化失败: {e}，将禁用语义搜索功能")
            self.vector_index = None
    
    def _build_vector_index(self):
        """构建向量索引"""
        if not self.vector_index or not self.embedding_model:
            return
        
        try:
            db = self.SessionLocal()
            try:
                # 获取所有已发布的知识条目
                nodes = db.query(KnowledgeGraphNode).filter(
                    KnowledgeGraphNode.node_metadata['status'].astext == 'published'
                ).all()
                
                if not nodes:
                    # 清空现有索引
                    self.id_to_index.clear()
                    self.index_to_id.clear()
                    if hasattr(self.vector_index, 'reset'):
                        self.vector_index.reset()
                    return
                
                # 为每个节点生成嵌入向量并添加到索引中
                embeddings = []
                ids = []
                
                for i, node in enumerate(nodes):
                    # 使用已存在的嵌入向量，如果不存在则跳过
                    if node.embedding is not None:
                        embeddings.append(node.embedding)
                        ids.append(int(uuid.UUID(str(node.id))))  # FAISS需要int64类型的ID
                        self.id_to_index[str(node.id)] = i
                        self.index_to_id[i] = str(node.id)
                
                # 清空现有索引
                self.id_to_index.clear()
                self.index_to_id.clear()
                
                # 重新添加所有向量
                if embeddings:
                    embeddings_array = np.array(embeddings).astype('float32')
                    # 确保数组是连续的
                    if not embeddings_array.flags['C_CONTIGUOUS']:
                        embeddings_array = np.ascontiguousarray(embeddings_array)
                    
                    # 重置索引
                    if hasattr(self.vector_index, 'reset'):
                        self.vector_index.reset()
                    
                    # 添加向量和ID
                    ids_array = np.array(ids).astype('int64')
                    # 确保数组是连续的
                    if not embeddings_array.flags['C_CONTIGUOUS']:
                        embeddings_array = np.ascontiguousarray(embeddings_array)
                    if hasattr(self.vector_index, 'add_with_ids'):
                        self.vector_index.add_with_ids(len(ids), embeddings_array, ids_array)  
                    else:
                        self.vector_index.add(embeddings_array, ids_array)
                    
                    # 重新建立ID映射
                    for i, item_id in enumerate(ids):
                        str_id = str(item_id)
                        self.id_to_index[str_id] = i
                        self.index_to_id[i] = str_id
                    
                    logger.info(f"向量索引构建完成，共添加 {len(embeddings)} 个条目")
            finally:
                db.close()
        except Exception as e:
            logger.error(f"构建向量索引失败: {e}")
    
    @lru_cache(maxsize=128)
    def _get_cache_key_hash(self, cache_key: str) -> str:
        """获取缓存键的哈希值"""
        return hashlib.md5(cache_key.encode()).hexdigest()
    
    def _generate_cache_key(self, query: KnowledgeBaseQuery) -> str:
        """生成缓存键"""
        key_parts = [
            query.query_text or "",
            query.category or "",
            ",".join(sorted(query.tags)) if query.tags else "",
            query.author or "",
            query.status or "",
            query.language or "",
            query.sort_by,
            query.sort_order,
            str(query.limit),
            str(query.offset),
            str(query.semantic_search),
            str(query.hybrid_search)
        ]
        base_key = f"kb_search:{hash(':'.join(key_parts))}"
        return self._get_cache_key_hash(base_key)
    
    def invalidate_cache_by_category(self, category: str):
        """根据分类使缓存失效"""
        # 清除LRU缓存
        self._get_cache_key_hash.cache_clear()
        logger.info(f"已清除分类 {category} 相关的缓存")
    
    def invalidate_cache_by_tag(self, tag: str):
        """根据标签使缓存失效"""
        # 清除LRU缓存
        self._get_cache_key_hash.cache_clear()
        logger.info(f"已清除标签 {tag} 相关的缓存")

    def create_knowledge_item(self, item: KnowledgeBaseItem) -> KnowledgeBaseItem:
        """
        创建AI知识库条目
        
        Args:
            item: AI知识库条目
            
        Returns:
            创建的AI知识库条目
        """
        # 确保条目有ID
        if not item.id:
            item.id = str(uuid.uuid4())
            
        db = self.SessionLocal()
        try:
            # 生成嵌入向量
            if self.embedding_model and item.status == "published":
                text_for_embedding = f"{item.title} {item.content}"
                try:
                    embedding = self.embedding_model.encode([text_for_embedding])[0].tolist()
                    item.embedding = embedding
                except Exception as e:
                    logger.warning(f"生成嵌入向量失败: {e}")
                    logger.debug(f"详细错误信息: {traceback.format_exc()}")
                    item.embedding = None
            else:
                item.embedding = None
            
            # 如果没有提供摘要，自动生成摘要
            if not item.summary and self.embedding_model:
                # 简单摘要：取内容前200个字符
                item.summary = item.content[:200] + "..." if len(item.content) > 200 else item.content
            
            # 创建知识节点
            # 安全处理metadata以避免Pylance类型检查错误
            metadata_dict = {}
            if isinstance(item.metadata, dict):
                metadata_dict = item.metadata
            elif hasattr(item.metadata, '__iter__'):
                try:
                    metadata_dict = dict(item.metadata)
                except (TypeError, ValueError):
                    metadata_dict = {}
            
            node_metadata = {
                "title": item.title,
                "category": item.category,
                "tags": item.tags,
                "author": item.author,
                "status": item.status,
                "version": item.version,
                "language": item.language,
                "summary": item.summary,
                "related_items": item.related_items,
                "view_count": item.view_count,
                "published_at": item.published_at.isoformat() if item.published_at else None,
                **metadata_dict
            }
            
            knowledge_node = KnowledgeGraphNode(  
                id=uuid.UUID(item.id),  # 显式设置ID
                content=str(item.content),  # type: ignore
                content_type="text",
                embedding=item.embedding if isinstance(item.embedding, list) else None,  # type: ignore
                node_metadata=node_metadata  # type: ignore
            )
            
            db.add(knowledge_node)
            db.commit()
            db.refresh(knowledge_node)
            
            # 转换为KnowledgeBaseItem
            result = KnowledgeBaseItem(
                id=str(knowledge_node.id),
                title=item.title,
                content=item.content,
                category=item.category,
                tags=item.tags,
                author=item.author,
                status=item.status,
                metadata=item.metadata,
                embedding=item.embedding,
                language=item.language,
                summary=item.summary,
                related_items=item.related_items,
                view_count=item.view_count,
                version=item.version,
                created_at=knowledge_node.created_at if isinstance(knowledge_node.created_at, datetime) else None,
                updated_at=knowledge_node.updated_at if isinstance(knowledge_node.updated_at, datetime) else None,
                published_at=item.published_at
            )
            
            # 更新向量索引（如果条目已发布）
            if self.embedding_model and self.vector_index and item.status == "published":
                self._update_vector_index(result)  # type: ignore
            
            # 同步到知识图谱（异步）
            try:
                asyncio.get_event_loop().run_in_executor(
                    self.executor, 
                    self._sync_to_knowledge_graph,  # type: ignore
                    result
                )
            except Exception as e:  # type: ignore
                logger.warning(f"异步同步到知识图谱失败: {e}")
                logger.debug(f"详细错误信息: {traceback.format_exc()}")
            
            return result
        except Exception as e:
            db.rollback()
            logger.error(f"创建知识库条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            raise ServiceError("KnowledgeBaseService", f"创建知识库条目失败: {str(e)}")
        finally:
            db.close()

    def get_search_suggestions(self, prefix: str, limit: int = 10) -> List[SearchSuggestion]:
        """
        获取搜索建议
        
        Args:
            prefix: 查询前缀
            limit: 返回建议数量限制
            
        Returns:
            搜索建议列表
        """
        try:
            suggestions = []
            prefix_lower = prefix.lower()
            
            # 根据前缀匹配搜索建议
            matched_suggestions = [
                (text, frequency) 
                for text, frequency in self.search_suggestions.items() 
                if text.lower().startswith(prefix_lower)
            ]
            
            # 按频率排序并限制数量
            matched_suggestions.sort(key=lambda x: x[1], reverse=True)
            limited_suggestions = matched_suggestions[:limit]
            
            # 转换为SearchSuggestion对象
            for text, frequency in limited_suggestions:
                suggestions.append(SearchSuggestion(text=text, frequency=frequency))
                
            return suggestions
        except Exception as e:
            logger.error(f"获取搜索建议失败: {e}")
            return []

    def search_knowledge_items(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]:
        """
        搜索知识库条目
        
        Args:
            query: 知识库查询对象
            
        Returns:
            知识库条目列表
        """
        try:
            # 检查是否启用语义搜索
            if query.semantic_search and self.embedding_model and self.vector_index:
                return self._semantic_search(query)
            
            # 检查是否启用混合搜索
            if query.hybrid_search and self.embedding_model and self.vector_index:
                return self._hybrid_search(query)
            
            # 默认使用数据库搜索
            return self._search_knowledge_items_from_db(query)
        except Exception as e:
            logger.error(f"搜索知识库条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            return []

    def _search_knowledge_items_from_db(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]:
        """
        从数据库搜索知识库条目
        
        Args:
            query: 知识库查询对象
            
        Returns:
            知识库条目列表
        """
        # 生成缓存键
        cache_key = self._generate_cache_key(query)
        
        # 尝试从缓存获取结果
        cached_result = self.cache_service.get(cache_key)
        if cached_result:
            logger.debug(f"从缓存获取搜索结果: {cache_key}")
            return cached_result if isinstance(cached_result, list) else []
        
        db = self.SessionLocal()
        try:
            # 构建查询
            db_query = db.query(KnowledgeGraphNode)
            
            # 添加过滤条件
            conditions = []
            
            # 文本搜索
            if query.query_text:
                # 简单的文本匹配，实际应用中可以使用全文搜索
                conditions.append(or_(
                    KnowledgeGraphNode.node_metadata['title'].astext.ilike(f"%{query.query_text}%"),
                    KnowledgeGraphNode.content.ilike(f"%{query.query_text}%")
                ))
            
            # 分类过滤
            if query.category:
                conditions.append(KnowledgeGraphNode.node_metadata['category'].astext == query.category)
            
            # 标签过滤
            if query.tags:
                # 标签匹配逻辑需要根据实际存储结构调整
                for tag in query.tags:
                    conditions.append(KnowledgeGraphNode.node_metadata['tags'].astext.ilike(f"%{tag}%"))
            
            # 作者过滤
            if query.author:
                conditions.append(KnowledgeGraphNode.node_metadata['author'].astext == query.author)
            
            # 状态过滤
            if query.status:
                conditions.append(KnowledgeGraphNode.node_metadata['status'].astext == query.status)
            
            # 语言过滤
            if query.language:
                conditions.append(KnowledgeGraphNode.node_metadata['language'].astext == query.language)
            
            # 应用所有条件
            if conditions:
                db_query = db_query.filter(and_(*conditions))
            
            # 排序
            if query.sort_by == "created_at":
                order_column = KnowledgeGraphNode.created_at
            elif query.sort_by == "updated_at":
                order_column = KnowledgeGraphNode.updated_at
            elif query.sort_by == "view_count":
                order_column = KnowledgeGraphNode.node_metadata['view_count'].astext.cast(Integer)
            else:
                order_column = KnowledgeGraphNode.created_at
            
            if query.sort_order == "desc":
                db_query = db_query.order_by(order_column.desc())
            else:
                db_query = db_query.order_by(order_column.asc())
            
            # 分页
            db_query = db_query.offset(query.offset).limit(query.limit)
            
            # 执行查询
            nodes = db_query.all()
            
            # 转换为KnowledgeBaseItem对象
            items = []
            for node in nodes:
                try:
                    item = KnowledgeBaseItem(
                        id=str(node.id),
                        title=node.node_metadata.get("title", ""),
                        content=str(node.content),
                        category=node.node_metadata.get("category", "default"),
                        tags=node.node_metadata.get("tags", []),
                        author=node.node_metadata.get("author"),
                        status=node.node_metadata.get("status", "draft"),
                        version=node.node_metadata.get("version", 1),
                        metadata={k: v for k, v in node.node_metadata.items() 
                                 if k not in ["title", "category", "tags", "author", "status", "version", "language", "summary", "related_items", "view_count", "published_at"]},
                        embedding=node.embedding if isinstance(node.embedding, list) else None,
                        language=node.node_metadata.get("language", "zh"),
                        summary=node.node_metadata.get("summary"),
                        related_items=node.node_metadata.get("related_items", []),
                        view_count=node.node_metadata.get("view_count", 0),
                        created_at=node.created_at if isinstance(node.created_at, datetime) else None,
                        updated_at=node.updated_at if isinstance(node.updated_at, datetime) else None,
                        published_at=datetime.fromisoformat(node.node_metadata.get("published_at")) if node.node_metadata.get("published_at") else None
                    )
                    items.append(item)
                except Exception as e:
                    logger.warning(f"转换知识条目失败: {e}")
                    continue
            
            # 缓存结果
            self.cache_service.set(cache_key, items, expire=300)  # 缓存5分钟
            
            return items
        except Exception as e:
            logger.error(f"从数据库搜索知识条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            return []
        finally:
            db.close()

    def _semantic_search(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]:
        """
        语义搜索
        
        Args:
            query: 知识库查询对象
            
        Returns:
            知识库条目列表
        """
        try:
            if not self.embedding_model or not self.vector_index:
                # 如果没有嵌入模型或向量索引，回退到数据库搜索
                logger.warning("嵌入模型或向量索引不可用，回退到数据库搜索")
                return self._search_knowledge_items_from_db(query)
            
            # 生成查询文本的嵌入向量
            query_embedding = self.embedding_model.encode([query.query_text])[0].astype('float32')
            
            # 执行向量搜索
            k = query.limit
            # 预分配结果数组
            distances = np.empty((1, k), dtype=np.float32)
            indices = np.empty((1, k), dtype=np.int64)
            # 使用正确的FAISS IndexIDMap search方法调用（使用位置参数）
            self.vector_index.search(query_embedding.reshape(1, -1), k, None, distances, indices)
            
            # 获取匹配的条目ID
            matched_ids = []
            for i, idx in enumerate(indices[0]):
                if idx != -1 and distances[0][i] <= query.similarity_threshold:  # 检查相似度阈值
                    if idx in self.index_to_id:
                        matched_ids.append(self.index_to_id[idx])
            
            if not matched_ids:
                return []
            
            # 从数据库获取完整条目
            db = self.SessionLocal()
            try:
                nodes = db.query(KnowledgeGraphNode).filter(
                    KnowledgeGraphNode.id.in_([uuid.UUID(id_) for id_ in matched_ids])
                ).all()
                
                # 转换为KnowledgeBaseItem对象
                items = []
                for node in nodes:
                    try:
                        item = KnowledgeBaseItem(
                            id=str(node.id),
                            title=node.node_metadata.get("title", ""),
                            content=str(node.content),
                            category=node.node_metadata.get("category", "default"),
                            tags=node.node_metadata.get("tags", []),
                            author=node.node_metadata.get("author"),
                            status=node.node_metadata.get("status", "draft"),
                            version=node.node_metadata.get("version", 1),
                            metadata={k: v for k, v in node.node_metadata.items() 
                                     if k not in ["title", "category", "tags", "author", "status", "version", "language", "summary", "related_items", "view_count", "published_at"]},
                            embedding=node.embedding if isinstance(node.embedding, list) else None,
                            language=node.node_metadata.get("language", "zh"),
                            summary=node.node_metadata.get("summary"),
                            related_items=node.node_metadata.get("related_items", []),
                            view_count=node.node_metadata.get("view_count", 0),
                            created_at=node.created_at if isinstance(node.created_at, datetime) else None,
                            updated_at=node.updated_at if isinstance(node.updated_at, datetime) else None,
                            published_at=datetime.fromisoformat(node.node_metadata.get("published_at")) if node.node_metadata.get("published_at") else None
                        )
                        items.append(item)
                    except Exception as e:
                        logger.warning(f"转换知识条目失败: {e}")
                        continue
                
                return items
            finally:
                db.close()
        except Exception as e:
            logger.error(f"语义搜索失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            # 出错时回退到数据库搜索
            return self._search_knowledge_items_from_db(query)

    def _hybrid_search(self, query: KnowledgeBaseQuery) -> List[KnowledgeBaseItem]:
        """
        混合搜索（关键词+语义）
        
        Args:
            query: 知识库查询对象
            
        Returns:
            知识库条目列表
        """
        try:
            # 执行关键词搜索
            keyword_results = self._search_knowledge_items_from_db(query)
            
            # 执行语义搜索
            semantic_results = self._semantic_search(query)
            
            # 合并结果并去重
            merged_results = {}
            
            # 添加关键词搜索结果
            for item in keyword_results:
                merged_results[item.id] = item
            
            # 添加语义搜索结果
            for item in semantic_results:
                if item.id not in merged_results:
                    merged_results[item.id] = item
            
            # 转换为列表并限制数量
            results = list(merged_results.values())
            return results[:query.limit]
        except Exception as e:
            logger.error(f"混合搜索失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            # 出错时回退到数据库搜索
            return self._search_knowledge_items_from_db(query)

    def get_knowledge_item(self, item_id: str, increment_view_count: bool = True) -> Optional[KnowledgeBaseItem]:
        """
        根据ID获取知识库条目
        
        Args:
            item_id: 知识条目ID
            increment_view_count: 是否增加查看次数
            
        Returns:
            知识库条目，如果未找到则返回None
        """
        db = self.SessionLocal()
        try:
            # 查询知识节点
            node = db.query(KnowledgeGraphNode).filter(KnowledgeGraphNode.id == uuid.UUID(item_id)).first()
            
            if not node:
                return None
            
            # 增加查看次数
            if increment_view_count:
                try:
                    # 修复 Pylance 类型检查错误，正确处理 SQLAlchemy Column 类型
                    current_metadata = node.node_metadata if isinstance(node.node_metadata, dict) else {}
                    if not current_metadata and node.node_metadata is not None and hasattr(node.node_metadata, '__dict__'):
                        # 如果 node.node_metadata 不是 dict 类型，尝试转换
                        try:
                            current_metadata = dict(node.node_metadata) if hasattr(node.node_metadata, '__iter__') else {}
                        except (TypeError, ValueError):
                            current_metadata = {}
                    
                    view_count = current_metadata.get("view_count", 0)
                    current_metadata["view_count"] = view_count + 1
                    node.node_metadata = current_metadata
                    db.commit()
                except Exception as e:
                    logger.warning(f"更新查看次数失败: {e}")
                    db.rollback()
            
            # 转换为KnowledgeBaseItem对象
            try:
                # 安全地处理node_metadata字段，避免Pylance类型检查错误
                node_metadata = node.node_metadata if isinstance(node.node_metadata, dict) else {}
                
                item = KnowledgeBaseItem(
                    id=str(node.id),
                    title=node_metadata.get("title", "") if node_metadata else "",
                    content=str(node.content),
                    category=node_metadata.get("category", "default") if node_metadata else "default",
                    tags=node_metadata.get("tags", []) if node_metadata else [],
                    author=node_metadata.get("author") if node_metadata else None,
                    status=node_metadata.get("status", "draft") if node_metadata else "draft",
                    version=node_metadata.get("version", 1) if node_metadata else 1,
                    metadata={k: v for k, v in node_metadata.items() 
                             if k not in ["title", "category", "tags", "author", "status", "version", "language", "summary", "related_items", "view_count", "published_at"]} if node_metadata else {},
                    embedding=node.embedding if isinstance(node.embedding, list) else None,
                    language=node_metadata.get("language", "zh") if node_metadata else "zh",
                    summary=node_metadata.get("summary") if node_metadata else None,
                    related_items=node_metadata.get("related_items", []) if node_metadata else [],
                    view_count=node_metadata.get("view_count", 0) if node_metadata else 0,
                    created_at=node.created_at if isinstance(node.created_at, datetime) else None,
                    updated_at=node.updated_at if isinstance(node.updated_at, datetime) else None,
                    published_at=datetime.fromisoformat(node_metadata.get("published_at")) if node_metadata and node_metadata.get("published_at") else None
                )
                return item
            except Exception as e:
                logger.warning(f"转换知识条目失败: {e}")
                return None
        except Exception as e:
            logger.error(f"获取知识条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            return None
        finally:
            db.close()

    def update_knowledge_item(self, item_id: str, item: KnowledgeBaseItem) -> KnowledgeBaseItem:
        """
        更新AI知识库条目
        
        Args:
            item_id: 知识条目ID
            item: AI知识库条目
            
        Returns:
            更新后的AI知识库条目
        """
        db = self.SessionLocal()
        try:
            # 查询知识节点
            node = db.query(KnowledgeGraphNode).filter(KnowledgeGraphNode.id == uuid.UUID(item_id)).first()
            
            if not node:
                raise ServiceError("KnowledgeBaseService", f"知识条目 {item_id} 未找到")
            
            # 更新嵌入向量
            if self.embedding_model and item.status == "published":
                text_for_embedding = f"{item.title} {item.content}"
                try:
                    embedding = self.embedding_model.encode([text_for_embedding])[0].tolist()
                    item.embedding = embedding
                except Exception as e:
                    logger.warning(f"生成嵌入向量失败: {e}")
                    logger.debug(f"详细错误信息: {traceback.format_exc()}")
                    item.embedding = None
            else:
                item.embedding = None
            
            # 如果没有提供摘要，自动生成摘要
            if not item.summary and self.embedding_model:
                # 简单摘要：取内容前200个字符
                item.summary = item.content[:200] + "..." if len(item.content) > 200 else item.content
            
            # 更新知识节点
            node.content = str(item.content)
            node.content_type = "text"
            node.embedding = item.embedding
            node.node_metadata = {
                "title": item.title,
                "category": item.category,
                "tags": item.tags,
                "author": item.author,
                "status": item.status,
                "version": item.version,
                "language": item.language,
                "summary": item.summary,
                "related_items": item.related_items,
                "view_count": item.view_count,
                "published_at": item.published_at.isoformat() if item.published_at else None,
                **item.metadata
            }
            
            db.commit()
            db.refresh(node)
            
            # 转换为KnowledgeBaseItem
            result = KnowledgeBaseItem(
                id=str(node.id),
                title=item.title,
                content=item.content,
                category=item.category,
                tags=item.tags,
                author=item.author,
                status=item.status,
                metadata=item.metadata,
                embedding=item.embedding,
                language=item.language,
                summary=item.summary,
                related_items=item.related_items,
                view_count=item.view_count,
                version=item.version,
                created_at=knowledge_node.created_at if isinstance(knowledge_node.created_at, datetime) else None,
                updated_at=knowledge_node.updated_at if isinstance(knowledge_node.updated_at, datetime) else None,
                published_at=item.published_at
            )
            
            # 更新向量索引（如果条目已发布）
            if self.embedding_model and self.vector_index and item.status == "published":
                self._update_vector_index(result)  # type: ignore
            
            # 同步到知识图谱（异步）
            try:
                asyncio.get_event_loop().run_in_executor(
                    self.executor, 
                    self._sync_to_knowledge_graph,  # type: ignore
                    result
                )
            except Exception as e:  # type: ignore
                logger.warning(f"异步同步到知识图谱失败: {e}")
                logger.debug(f"详细错误信息: {traceback.format_exc()}")
            
            return result
        except Exception as e:
            db.rollback()
            logger.error(f"更新知识库条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            raise ServiceError("KnowledgeBaseService", f"更新知识库条目失败: {str(e)}")
        finally:
            db.close()

    def delete_knowledge_item(self, item_id: str) -> None:
        """
        删除AI知识库条目
        
        Args:
            item_id: 知识条目ID
        """
        db = self.SessionLocal()
        try:
            # 查询知识节点
            node = db.query(KnowledgeGraphNode).filter(KnowledgeGraphNode.id == uuid.UUID(item_id)).first()
            
            if not node:
                raise ServiceError("KnowledgeBaseService", f"知识条目 {item_id} 未找到")
            
            # 删除知识节点
            db.delete(node)
            db.commit()
            
            # 从向量索引中移除
            if self.embedding_model and self.vector_index:
                self._remove_from_vector_index(item_id)  # type: ignore
            
            # 从知识图谱中移除（异步）
            try:
                asyncio.get_event_loop().run_in_executor(
                    self.executor, 
                    self._sync_to_knowledge_graph,  # type: ignore
                    None,
                    item_id
                )
            except Exception as e:
                logger.warning(f"异步更新知识图谱失败: {e}")
        except Exception as e:
            db.rollback()
            logger.error(f"删除知识库条目失败: {e}")
            logger.debug(f"详细错误信息: {traceback.format_exc()}")
            raise ServiceError("KnowledgeBaseService", f"删除知识库条目失败: {str(e)}")
        finally:
            db.close()